package com.polarrose.wsf.accesscontrol;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IpUtils {

	private static final Pattern IP_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)(/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))?$");

	public static class IpAndMask {
		private int ip;
		private int mask;

		public IpAndMask(int ip, int mask) {
			this.ip = ip;
			this.mask = mask;
		}

		public int getIp() {
			return ip;
		}

		public void setIp(int ip) {
			this.ip = ip;
		}

		public int getMask() {
			return mask;
		}

		public void setMask(int mask) {
			this.mask = mask;
		}

		public boolean containsIp(int theip) {
			return (this.ip & this.mask) == (theip & this.mask);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ip;
			result = prime * result + mask;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			IpAndMask other = (IpAndMask) obj;
			if (ip != other.ip)
				return false;
			if (mask != other.mask)
				return false;
			return true;
		}

	}

	public static boolean isIP(String address) {
		Matcher matcher = IP_PATTERN.matcher(address);
		return matcher.matches();
	}

	/**
	 * @param ip
	 * @return array with ip at index 0 and mask at index 1
	 */
	public static IpAndMask parseIP(String ip) {
		int ipek = 0;
		int mask = 0xFFFFFFFF;
		Matcher matcher = IP_PATTERN.matcher(ip);

		if (matcher.matches()) {
			ipek = createIpFromMatcher(matcher);
			if (matcher.group(5) != null) {
				mask = createMaskFromMatcher(matcher);
			}
		}
		return new IpAndMask(ipek, mask);
	}

	private static int createMaskFromMatcher(Matcher matcher) {
		return ((Integer.parseInt(matcher.group(6)) & 0xFF) << 24)
			 | ((Integer.parseInt(matcher.group(7)) & 0xFF) << 16)
			 | ((Integer.parseInt(matcher.group(8)) & 0xFF) << 8)
			 | (Integer.parseInt(matcher.group(9)) & 0xFF);

	}

	private static int createIpFromMatcher(Matcher matcher) {
		return ((Integer.parseInt(matcher.group(1)) & 0xFF) << 24) 
			 | ((Integer.parseInt(matcher.group(2)) & 0xFF) << 16)
			 | ((Integer.parseInt(matcher.group(3)) & 0xFF) << 8)
			 | (Integer.parseInt(matcher.group(4)) & 0xFF);
	}

	public static boolean addressMatches(String address, String[] addresses) {
		int addressIp = parseIP(address).ip;
		IpAndMask[] addressesIpsAndMasks = new IpAndMask[addresses.length];
		for (int i = 0; i < addresses.length; i++) {
			IpAndMask parseIP = parseIP(addresses[i]);
			addressesIpsAndMasks[i] = parseIP;
		}
		return addressMatches(addressIp, addressesIpsAndMasks);
	}

	public static boolean addressMatches(int address, IpAndMask[] ipsAndMasks) {
		for (IpAndMask ipAndMask : ipsAndMasks) {
			if (ipAndMask.containsIp(address)) {
				return true;
			}
		}
		return false;
	}

	public static IpAndMask createIpFromInetAddress(String host) throws UnknownHostException {
		byte[] ipBytes = InetAddress.getByName(host).getAddress();
		int ip = ((ipBytes[0] & 0xFF) << 24) 
			| ((ipBytes[1] & 0xFF) << 16)
			| ((ipBytes[2] & 0xFF) << 8)
			| (ipBytes[3] & 0xFF);
		return new IpAndMask(ip, 0xFFFFFFFF);
	}
}
